package wiki.xsx.core.handler;

import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import wiki.xsx.core.config.RedisProperties;
import wiki.xsx.core.util.ApplicationContextUtil;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 通用助手
 * @author xsx
 * @date 2019/4/25
 * @since 1.8
 */
public class CommonHandler {
    /**
     * redis模板(用于对象)
     */
    private static final RedisTemplate<String, Object> REDIS_TEMPLATE = ApplicationContextUtil.getContext().getBean("redisTemplate", RedisTemplate.class);
    /**
     * redis模板(用于字符串)
     */
    private static final StringRedisTemplate STRING_REDIS_TEMPLATE = ApplicationContextUtil.getContext().getBean("stringRedisTemplate", StringRedisTemplate.class);
    /**
     * 助手实例
     */
    private static final CommonHandler HANDLER = new CommonHandler();
    /**
     * 默认KEY
     */
    private static final String DEFAULT_KEY = "default";

    /**
     * 数据库助手字典
     */
    private final ConcurrentMap<String, DBHandler> dbMap= new ConcurrentHashMap<>(13);
    /**
     * 键助手字典
     */
    private final ConcurrentMap<String, KeyHandler> keyMap= new ConcurrentHashMap<>(13);
    /**
     * 字符串类型助手字典
     */
    private final ConcurrentMap<String, StringTypeHandler> stringTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 列表类型助手字典
     */
    private final ConcurrentMap<String, ListTypeHandler> listTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 哈希类型助手字典
     */
    private final ConcurrentMap<String, HashTypeHandler> hashTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 无序集合类型助手字典
     */
    private final ConcurrentMap<String, SetTypeHandler> setTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 有序集合类型助手字典
     */
    private final ConcurrentMap<String, ZsetTypeHandler> zsetTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 位图类型助手字典
     */
    private final ConcurrentMap<String, BitmapTypeHandler> bitmapTypeMap= new ConcurrentHashMap<>(13);
    /**
     * 基数类型助手字典
     */
    private final ConcurrentMap<String, HyperLogLogTypeHandler> hyperLogLogTypeMap= new ConcurrentHashMap<>(13);

    /**
     * 通用助手构造
     */
    private CommonHandler() {
        int dbIndex = getInitDBIndex();
        initDBHandler(dbIndex);
        initKeyHandler(dbIndex);
        initStringTypeHandler(dbIndex);
        initListTypeHandler(dbIndex);
        initHashTypeHandler(dbIndex);
        initSetTypeHandler(dbIndex);
        initZsetTypeHandler(dbIndex);
        initBitmapTypeHandler(dbIndex);
        initHyperLogLogTypeHandler(dbIndex);
    }

    /**
     * 获取助手实例
     * @return 返回助手实例
     */
    public static CommonHandler getInstance() {
        return HANDLER;
    }

    /**
     * 获取默认KEY
     * @return 返回默认KEY
     */
    public String getDefaultKey() {
        return DEFAULT_KEY;
    }

    /**
     * 获取数据库助手
     * @param key KEY
     * @return 返回数据库助手
     */
    public DBHandler getDBHandler(String key) {
        DBHandler handler = this.dbMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.dbMap) {
            handler = this.dbMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                DBHandler instance = DBHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.dbMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取键助手
     * @param key KEY
     * @return 返回键助手
     */
    public KeyHandler getKeyHandler(String key) {
        KeyHandler handler = this.keyMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.keyMap) {
            handler = this.keyMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                KeyHandler instance = KeyHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.keyMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取字符串类型助手
     * @param key KEY
     * @return 返回字符串类型助手
     */
    public StringTypeHandler getStringTypeHandler(String key) {
        StringTypeHandler handler = this.stringTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.stringTypeMap) {
            handler = this.stringTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                StringTypeHandler instance = StringTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.stringTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取列表类型助手
     * @param key KEY
     * @return 返回列表类型助手
     */
    public ListTypeHandler getListTypeHandler(String key) {
        ListTypeHandler handler = this.listTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.listTypeMap) {
            handler = this.listTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                ListTypeHandler instance = ListTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.listTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取哈希类型助手
     * @param key KEY
     * @return 返回哈希类型助手
     */
    public HashTypeHandler getHashTypeHandler(String key) {
        HashTypeHandler handler = this.hashTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.hashTypeMap) {
            handler = this.hashTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                HashTypeHandler instance = HashTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.hashTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取无序集合类型助手
     * @param key KEY
     * @return 返回无序集合类型助手
     */
    public SetTypeHandler getSetTypeHandler(String key) {
        SetTypeHandler handler = this.setTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.setTypeMap) {
            handler = this.setTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                SetTypeHandler instance = SetTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.setTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取有序集合类型助手
     * @param key KEY
     * @return 返回有序集合类型助手
     */
    public ZsetTypeHandler getZsetTypeHandler(String key) {
        ZsetTypeHandler handler = this.zsetTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.zsetTypeMap) {
            handler = this.zsetTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                ZsetTypeHandler instance = ZsetTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.zsetTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取位图类型助手
     * @param key KEY
     * @return 返回位图类型助手
     */
    public BitmapTypeHandler getBitmapTypeHandler(String key) {
        BitmapTypeHandler handler = this.bitmapTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.bitmapTypeMap) {
            handler = this.bitmapTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                BitmapTypeHandler instance = BitmapTypeHandler.getInstance(this.createStringRedisTemplate(dbIndex));
                handler = this.bitmapTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取基数类型助手
     * @param key KEY
     * @return 返回基数类型助手
     */
    public HyperLogLogTypeHandler getHyperLogLogTypeHandler(String key) {
        HyperLogLogTypeHandler handler = this.hyperLogLogTypeMap.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (this.hyperLogLogTypeMap) {
            handler = this.hyperLogLogTypeMap.get(key);
            if (handler==null) {
                int dbIndex = Integer.valueOf(key);
                HyperLogLogTypeHandler instance = HyperLogLogTypeHandler.getInstance(this.createRedisTemplate(dbIndex), this.createStringRedisTemplate(dbIndex));
                handler = this.hyperLogLogTypeMap.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取默认对象模板
     * @return 返回对象模板
     */
    public RedisTemplate<String, Object> getDefaultRedisTemplate() {
        return REDIS_TEMPLATE;
    }

    /**
     * 获取默认字符串模板
     * @return 返回字符串模板
     */
    public StringRedisTemplate getDefaultStringRedisTemplate() {
        return STRING_REDIS_TEMPLATE;
    }

    /**
     * 获取连接工厂
     * @param dbIndex 数据库索引
     * @return 返回指定的连接工厂
     */
    public static RedisConnectionFactory getConnectionFactory(int dbIndex) {
        RedisConnectionFactory redisConnectionFactory = ApplicationContextUtil.getContext().getBean("redisConnectionFactory", RedisConnectionFactory.class);
        if (redisConnectionFactory instanceof JedisConnectionFactory) {
            ((JedisConnectionFactory) redisConnectionFactory).setDatabase(dbIndex);
        }else if (redisConnectionFactory instanceof LettuceConnectionFactory) {
            ((LettuceConnectionFactory) redisConnectionFactory).setDatabase(dbIndex);
        }else {
            throw new RuntimeException("no support connection factory");
        }
        return redisConnectionFactory;
    }

    /**
     * 获取初始化数据库索引
     * @return 返回数据库索引
     */
    private int getInitDBIndex() {
        return ApplicationContextUtil.getContext().getBean(RedisProperties.class).getDatabase();
    }

    /**
     * 初始化数据库助手
     * @param dbIndex 数据库索引
     */
    private void initDBHandler(int dbIndex) {
        DBHandler handler = DBHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.dbMap.put(DEFAULT_KEY, handler);
        this.dbMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化键助手
     * @param dbIndex 数据库索引
     */
    private void initKeyHandler(int dbIndex) {
        KeyHandler handler = KeyHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.keyMap.put(DEFAULT_KEY, handler);
        this.keyMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化字符串类型助手
     * @param dbIndex 数据库索引
     */
    private void initStringTypeHandler(int dbIndex) {
        StringTypeHandler handler = StringTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.stringTypeMap.put(DEFAULT_KEY, handler);
        this.stringTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化列表类型助手
     * @param dbIndex 数据库索引
     */
    private void initListTypeHandler(int dbIndex) {
        ListTypeHandler handler = ListTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.listTypeMap.put(DEFAULT_KEY, handler);
        this.listTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化哈希类型助手
     * @param dbIndex 数据库索引
     */
    private void initHashTypeHandler(int dbIndex) {
        HashTypeHandler handler = HashTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.hashTypeMap.put(DEFAULT_KEY, handler);
        this.hashTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化无序集合类型助手
     * @param dbIndex 数据库索引
     */
    private void initSetTypeHandler(int dbIndex) {
        SetTypeHandler handler = SetTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.setTypeMap.put(DEFAULT_KEY, handler);
        this.setTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化有序集合类型助手
     * @param dbIndex 数据库索引
     */
    private void initZsetTypeHandler(int dbIndex) {
        ZsetTypeHandler handler = ZsetTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.zsetTypeMap.put(DEFAULT_KEY, handler);
        this.zsetTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化位图类型助手
     * @param dbIndex 数据库索引
     */
    private void initBitmapTypeHandler(int dbIndex) {
        BitmapTypeHandler handler = BitmapTypeHandler.getInstance(STRING_REDIS_TEMPLATE);
        this.bitmapTypeMap.put(DEFAULT_KEY, handler);
        this.bitmapTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化基数类型助手
     * @param dbIndex 数据库索引
     */
    private void initHyperLogLogTypeHandler(int dbIndex) {
        HyperLogLogTypeHandler handler = HyperLogLogTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
        this.hyperLogLogTypeMap.put(DEFAULT_KEY, handler);
        this.hyperLogLogTypeMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 创建对象模板
     * @return 返回对象模板
     */
    private RedisTemplate<String, Object> createRedisTemplate(int dbIndex) {
        return this.createTemplate(dbIndex, false);
    }

    /**
     * 创建字符串模板
     * @return 返回字符串模板
     */
    private StringRedisTemplate createStringRedisTemplate(int dbIndex) {
        return (StringRedisTemplate) this.createTemplate(dbIndex, true);
    }

    /**
     * 创建模板
     * @param dbIndex 数据库索引
     * @param isString 是否为字符串模板
     * @return 返回模板
     */
    private RedisTemplate createTemplate(int dbIndex, boolean isString) {
        return this.initRedisTemplate(getConnectionFactory(dbIndex), isString);
    }

    /**
     * 初始化模板
     * @param factory 连接工厂
     * @param isString 是否为字符串模板
     * @return 返回模板
     */
    private RedisTemplate initRedisTemplate(RedisConnectionFactory factory, boolean isString) {
        RedisTemplate redisTemplate;
        if (isString) {
            redisTemplate = new StringRedisTemplate(factory);
        }else{
            redisTemplate = new RedisTemplate<String, Object>();
            redisTemplate.setKeySerializer(REDIS_TEMPLATE.getKeySerializer());
            redisTemplate.setValueSerializer(REDIS_TEMPLATE.getValueSerializer());
            redisTemplate.setHashKeySerializer(REDIS_TEMPLATE.getHashKeySerializer());
            redisTemplate.setHashValueSerializer(REDIS_TEMPLATE.getHashValueSerializer());
            redisTemplate.setConnectionFactory(factory);
            redisTemplate.afterPropertiesSet();
        }
        return redisTemplate;
    }
}
